package trader.currency;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import trader.util.Instrument;
import trader.util.Util;

public class Trader {
    private static final ArrayList<Currency> currencies = new ArrayList<Currency>();
    private static final String REPLACE_ME = "@@@";
    private static final String SEARCH_URL = "/search.php?market=CURRENCY&underlying=" + REPLACE_ME + "/SEK&instrument_type=MiniFuture";
    
    static {
        currencies.add(new Currency("DKK", new String[] {"DRX", "DRV", "DRS"}));
        currencies.add(new Currency("GBP", new String[] {"DQX", "DCK", "DCL", "KC5", "KC3", "EBF", "EBC"}));
        currencies.add(new Currency("NOK", new String[] {"DRU", "DRQ", "DRT", "Q2J", "Q2H", "Q2K"}));
        currencies.add(new Currency("USD", new String[] {"CU9", "HNN", "CCX", "RL1", "C1G", "C1H", "C03", "RL0", "K77", "K7D", "K78", "JM8", "NU6", "RL2"}));
        currencies.add(new Currency("EUR", new String[] {"CXP", "HKR", "CCV", "RL3", "C1J", "C1K", "G5L", "DVV", "K75", "K74", "EBB", "JM7", "RL4"}));
        currencies.add(new Currency("CHF", new String[] {"H5J", "H5H", "DQV", "SNR", "SNS", "SNU", "SNT"}));
    }
    
    private String data_key;
    private HashMap<String, Instrument> instruments = new HashMap<String, Instrument>();
    
    public static void main(String[] args) {
        new Trader().run();
    }
    
    public void run() {
        if (!Util.login(Util.MAIL, Util.PASSWORD)) {
            System.out.println("Failed to login");
            return;
        }
        
        //Util.atomicCash.set((long)getPortfolioCash(Util.buyPortfolio));
        Util.cash = (long)getPortfolioCash(Util.buyPortfolio);
        data_key = getDataKey("DKK");
        for (Currency curr : currencies) {
            for (String id : curr.ids) {
                instruments.put(id, new Instrument(id));
            }
        }
        
        while (true) {
            long start = System.currentTimeMillis();
            for (Instrument instrument : instruments.values()) {
                updatePrice(instrument);
                logic(instrument);
            }
            long diff = 2000 - (System.currentTimeMillis() - start);
            if (diff > 0) {
                try {
                    System.out.println("Sleeping for " + diff + " ms");
                    Thread.sleep(diff);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
    
    private void updatePrice(Instrument instrument) {
        URL url = null;
        try {
            url = new URL(Util.BASE_URL + "/scripts/php/data/getdata.php");
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return;
        }
        try {
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            String data = "action=data_by_id&id=" + instrument.id + "&data_key=" + data_key;
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Cookie", Util.sessionId);
            conn.setDoOutput(true);
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.close();
            String line = "";
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            while ((line = in.readLine()) != null) {
                //System.out.println(instrument.id + ": " + line);
                Object obj = Util.fromJSon(line);
                if (obj instanceof JSONObject) {
                    data_key = (String)((JSONObject)obj).get("data_key");
                    Object instrObj = ((JSONObject)obj).get("instruments");
                    if (instrObj instanceof JSONArray) {
                        JSONArray instruments = (JSONArray)instrObj;
                        if (instruments.isEmpty()) {
                            continue;
                        }
                        if (instruments.size() != 1) {
                            System.out.println(instrument.id + ": Longer than 1: " + instruments);
                        }
                        JSONObject instr = (JSONObject)instruments.get(0);
                        instrument.prevPrice = instrument.currPrice;
                        if (instr.get("ask") instanceof Long) {
                            instrument.currPrice = (double)(Long)instr.get("ask");
                        } else if (instr.get("ask") instanceof Double) {
                            instrument.currPrice = (Double)instr.get("ask");
                        }
                    }
                }
            }
            in.close();
        } catch (IOException e) {
            System.out.println("Poller " + instrument.id + ": " + e);
        }
    }
    
    private void logic(Instrument instrument) {
        if (instrument.prevPrice == 0 || instrument.currPrice == 0) {
            return;
        }
        
        double diff = (instrument.prevPrice - instrument.currPrice) / instrument.prevPrice;
        
        if (Math.random() < 0.01) {
            System.out.println(instrument.id + " currPrice=" + instrument.currPrice + ", prevPrice=" + instrument.prevPrice + ", diff=" + diff);
        }
        
        if (diff > Util.buyLimit) {
            //long cash = Util.atomicCash.get();
            if (Util.cash < instrument.currPrice) {
                return;
            }
            int volume = (int)(Util.cash / instrument.currPrice);
            //if (Util.busy.getAndSet(true)) {
            //    System.out.println("Found (but already busy) " + instrument.id + ", currPrice=" + instrument.currPrice + ", prevPrice=" + instrument.prevPrice + ", diff=" + diff);
            //    return;
            //}
            Util.buy(instrument.id, instrument.currPrice, volume, Util.buyPortfolio);
            //Util.cashChanged.set(true);
            instrument.volume = volume;
            if (instrument.internalId == null) {
                instrument.internalId = getInternalId(instrument.id, Util.buyPortfolio);
            }
            System.out.println("Bought " + instrument.id + ", volume=" + volume + ", price=" + instrument.currPrice + ", prevPrice=" + instrument.prevPrice + ", diff=" + diff);
            getRidOf(instrument);
        } else if (diff > Util.buyLimit / 4) {
            System.out.println(instrument.id + ": prev=" + instrument.prevPrice + ", curr=" + instrument.currPrice + ", diff=" + diff);
        }
    }
    
    private void getRidOf(Instrument instrument) {
        int i = 0;
        while (true) {
            updatePrice(instrument);
            
            double diff = (instrument.currPrice - instrument.prevPrice) / instrument.prevPrice;
            
            if (diff > Util.buyLimit * 0.75) {
                Util.sell(instrument.id, instrument.internalId, instrument.currPrice, instrument.volume);
                System.out.println("Sold " + instrument.id + ", volume=" + instrument.volume + ", price=" + instrument.currPrice + ", prevPrice=" + instrument.prevPrice + ", diff=" + diff);
                //Util.busy.set(false);
                //Util.atomicCash.set((long)getPortfolioCash(Util.buyPortfolio));
                Util.cash = (long)getPortfolioCash(Util.buyPortfolio);
                return;
            }
            
            i++;
            if (i > 10) {
                System.out.println("Warning: " + instrument.id + " " + i + " (>10) iterations since buy");
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
            }
        }
    }
    
    private String getInternalId(String id, int portfolio) {
        try {
            URL url = new URL(Util.BASE_URL + "/scripts/php/data/getdata.php");
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            String data = "action=get_portfolio_content&portfolio_id=" + portfolio;
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Cookie", Util.sessionId);
            conn.setDoOutput(true);
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.close();
            String line = "";
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            while ((line = in.readLine()) != null) {
                if (line.equals("")) {
                    continue;
                }
                Object obj = Util.fromJSon(line);
                if (obj instanceof JSONObject) {
                    JSONObject jsonObj = (JSONObject)obj;
                    JSONObject content = (JSONObject)jsonObj.get("content");
                    for (Object o : content.keySet()) {
                        JSONObject jsonInstr = (JSONObject)content.get(o);
                        String rid = (String)jsonInstr.get("instrument_id");
                        if (rid.equals(id)) {
                            return (String)o;
                        }
                    }
                } else if (obj instanceof JSONArray) {
                    //Nothing in portfolio
                } else {
                    System.out.println("What is this? " + obj.getClass() + " : line");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(0);
        }
        return null;
    }
    
    private double getPortfolioCash(int portfolio) {
        URL url = null;
        try {
            url = new URL(Util.BASE_URL + "/portfolio_overview.php?port_id=" + portfolio);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Cookie", Util.sessionId);
            String line = "";
            String magic = "<td class=\"arial_11\" id=\"available_funds\" style=\"background-color:#FFFFFF; text-align:right\">";
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            while ((line = in.readLine()) != null) {
                line = line.trim();
                if (line.startsWith(magic)) {
                    double cash = Double.parseDouble(line.substring(magic.length(), line.length() - 5));
                    System.out.println("Updated cash: " + cash);
                    return cash;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return 0;
        }
        return 0;
    }
    
    private String getDataKey(String name) {
        URL url = null;
        try {
            url = new URL(Util.BASE_URL + SEARCH_URL.replaceAll(REPLACE_ME, name));
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
        try {
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Cookie", Util.sessionId);
            String line = "";
            String magic = "option = { action: 'data_by_id', data_key: ";
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            while ((line = in.readLine()) != null) {
                line = line.trim();
                if (line.startsWith(magic)) {
                    in.close();
                    return line.substring(line.indexOf("'", magic.length()) + 1, line.lastIndexOf("'"));
                }
            }
            in.close();
        } catch (IOException e) {
            System.out.println("getDataKey() failed: " + name + " " + e);
        }
        return null;
    }
}
